﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.ServiceModel;
using ProgNetComponentsDx.Options;
using System.Threading.Tasks;
using System.IO;
using ProgNetComponents.Data.SqlCe;
using System.Data;

namespace BuildHelper.Server
{
    public class Client : IUpdateServerServiceCallback
    {
        public static Client Instance { get; set; }
        public static void RunInstance()
        {
            Instance = new Client();
            Instance.Run();
        }
        public event MessageReceivedHandler MessageReceived;

        ProgNetComponentsDx.Tasks.BaseEventTask t = new ProgNetComponentsDx.Tasks.BaseEventTask();
        IUpdateServerService IUpdateServerServiceProxy;
        #region Properties
        public string Name
        {
            get;
            set;
        }
        public long Id
        {
            get;
            private set;
        }
        
        public bool AddLogs
        {
            get;
            set;
        }
        public string LogFile
        {
            get;
            private set;
        }
        public string HostIp
        {
            get { return OptionCache.GetOptionStringValue(Init.InitFormOptionNames.Kontakty_Group.Server.HostIp); }
        }
        public int Port
        {
            get { return OptionCache.GetOptionValue(Init.InitFormOptionNames.Kontakty_Group.Server.Port).AsInt(4297); }
        }
        #endregion

        public void Run()
        {
            t.Run();
        }

        public Client()
        {
            LogFile = Application.StartupPath + "\\Client.log";
            AddLogs = true;
            Name = Environment.UserName;
            t.Name = "Connect";
            t.TaskFinished += new EventHandler(t_TaskFinished);
            t.TaskPerformed += new ProgNetComponentsDx.Tasks.PerformTaskHandler(t_TaskPerformed);
        }
        public void Close()
        {
            t.Cancel();
        }
        void t_TaskFinished(object sender, EventArgs e)
        {
            try
            {
                IUpdateServerServiceProxy.Disconnect(Id);
            }
            catch { }

            OnMessageReceived("Finished: " + t.Status);
            ClearProxy();
        }

        void t_TaskPerformed(object sender, ProgNetComponentsDx.Tasks.TaskResultEventArgs e)
        {
            while (true)
            {
                e.ThrowIfCancellationRequested();

                if (!IsConnected())
                {
                    Id = -1;
                    Connect();
                }
                
                for (int i = 0; i < 10; i++)
                {
                    e.ThrowIfCancellationRequested();
                    System.Threading.Thread.Sleep(1000);
                }
            }
            
        }
        private void Connect()
        {
            try
            {
                string endPointAddr = "net.tcp://{0}:{1}/BHServer".FormatMe(HostIp, Port);
                NetTcpBinding tcpBinding = new NetTcpBinding();
                tcpBinding.TransactionFlow = false;
                tcpBinding.Security.Transport.ProtectionLevel = System.Net.Security.ProtectionLevel.EncryptAndSign;
                tcpBinding.Security.Transport.ClientCredentialType = TcpClientCredentialType.Windows;
                tcpBinding.Security.Mode = SecurityMode.None;

                OnMessageReceived("Łączenie z: " + endPointAddr);
                IUpdateServerServiceProxy = DuplexChannelFactory<IUpdateServerService>.CreateChannel(this, tcpBinding, new EndpointAddress(endPointAddr + "/Update"));
                Id = IUpdateServerServiceProxy.Connect(Name);
                OnMessageReceived("Połączono. Id: {0}, Name={1}".FormatMe(Id, Name));
            }
            catch (Exception ex)
            {
                OnMessageReceived(ex.Message + Constants.NewLineLinux + "Stack:" + ex.StackTrace);
                ClearProxy();
            }
        }
        private bool IsConnected()
        {
            if (IUpdateServerServiceProxy == null)
                return false;
            try
            {
                IUpdateServerServiceProxy.Beep();
                return true;
            }
            catch { }
            return false;
        }

        protected void OnMessageReceived(string msg)
        {
            if (MessageReceived != null)
                MessageReceived(msg);
            if (AddLogs)
                Task.Factory.StartNew(() => { "{0} {1}".FormatMe(DateTime.Now.ToString("T"), msg).AppendToFile(LogFile); });
        }
        private void ClearProxy()
        {
            IUpdateServerServiceProxy = null;
            Id = -1;
        }

        #region IUpdateServerServiceCallback
        public void NotifyNewVersion(Version newVersion)
        {
            //Version myVer = new Version(Application.ProductVersion);
            //if (myVer < newVersion)
            {
                FormMain.Instance.ShowDxInfoDialog("Dostępna jest nowa wersja: {0}".FormatMe(newVersion));
            }
        }

        public void Notify()
        {
           
        }

        public Version GetVersion()
        {
            return new Version(Application.ProductVersion);
        }

        public void Info(string message)
        {
            FormMain.Instance.ShowDxInfoDialog(message);
        }

        public void GetLogFile()
        {
            Task t = Task.Factory.StartNew(() =>
            {
                string file = Application.StartupPath + "\\error.log";
                try
                {
                    using (Stream s = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
                    {
                        SendStream(s, "LogFile");
                    }
                }
                catch { }
            });
        }
        public void GetDataTable(string select)
        {
            Task t = Task.Factory.StartNew(() =>
            {
                using (SqlCeDataManager m = new SqlCeDataManager())
                {
                    string tablename = select.GetFirstMatch(" FROM (\\w*) ");
                    if (tablename.IsNull())
                        tablename = "Table";
                    DataTable dt = new DataTable(tablename);
                    m.FillTable(dt, select);
                    using (MemoryStream ms = new MemoryStream())
                    {
                        try
                        {
                            dt.WriteXml(ms, XmlWriteMode.WriteSchema);
                        }
                        catch { }
                        finally
                        {
                            SendStream(ms, "DataTable");
                        }
                    }
                }
            });
        }
        public void ClearLogFile()
        {
            string file = Application.StartupPath + "\\error.log";
            "".WriteToFile(file);
        }
        #endregion

        private void SendStream(Stream s, string streamId)
        {
            if (IUpdateServerServiceProxy.BeginSendingStream(Id, streamId))
            {
                try
                {
                    const int bufferSize = 1024 * 10;
                    byte[] buffer = new byte[bufferSize];
                    try
                    {
                        s.Seek(0, SeekOrigin.Begin);
                    }
                    catch { }
                    int bytesRead = s.Read(buffer, 0, bufferSize);
                    while (bytesRead > 0)
                    {
                        IUpdateServerServiceProxy.SendStreamPart(Id, streamId, buffer, bufferSize);
                        bytesRead = s.Read(buffer, 0, bufferSize);
                    }
                }
                catch { }
                finally
                {
                    IUpdateServerServiceProxy.EndSendingStream(Id, streamId);
                }
            }
        }
        
    }
}
